﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

using WFL.Utils;

namespace WFL.Threading
{
    public class BatchRuntime
    {
        public string Name
        {
            get;
            private set;
        }

        public int MaxThreadCount
        {
            get;
            private set;
        }

        private Queue<BatchInstance> _waitingBatchInstances;
        private readonly object _waitingBatchInstancesLock = new object();

        private Dictionary<string, Batch> _batchs;
        private readonly object _batchsLock = new object();

        private AutoResetEvent _waitHandle;
        private Thread _scheduleThread;
        private List<BatchThread> _batchThreads;
        private bool _scheduling = false;
        private readonly object _scheduleLock = new object();

        public event LogEventHandler Log;


        public BatchRuntime(string name, int maxThreadCount)
        {
            Initialize(name, maxThreadCount);
        }

        public BatchRuntime(string configuration, string section, string name)
        {
            if (string.IsNullOrEmpty(section))
            {
                throw new ArgumentNullException("section", "指定的任务引擎配置节名称不能为空");
            }
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name", "指定的任务引擎名称不能为空");
            }
            BatchRuntimeConfigurationSection configurationSection = null;
            if (!string.IsNullOrEmpty(configuration))
            {
                configurationSection = ConfigurationHelper.GetSection<BatchRuntimeConfigurationSection>(configuration, section);
            }
            else
            {
                configurationSection = (BatchRuntimeConfigurationSection)ConfigurationManager.GetSection(section);
            }
            if (configurationSection == null)
            {
                throw new Exception("配置文件中找不到指定的任务引擎配置信息");
            }
            foreach (BatchRuntimeConfigurationElement batchRuntimeElement in configurationSection.Runtimes)
            {
                if (batchRuntimeElement.Name == name)
                {
                    Initialize(batchRuntimeElement.Name, batchRuntimeElement.MaxThreadCount);
                    if (batchRuntimeElement.Batches != null 
                        && batchRuntimeElement.Batches.Count > 0)
                    {
                        foreach (BatchConfigurationElement batchElement in batchRuntimeElement.Batches)
                        {
                            Type batchType = Type.GetType(batchElement.Type, true);
                            Batch batch = (Batch)Activator.CreateInstance(batchType, batchElement.Name, batchElement.Parameters);
                            if (batch is TimingBatch)
                            {
                                TimingBatch timingBatch = (TimingBatch)batch;
                                if (!string.IsNullOrEmpty(batchElement.StartTime))
                                {
                                    timingBatch.StartTime = DateTime.Parse(batchElement.StartTime);
                                }
                                if (!string.IsNullOrEmpty(batchElement.Interval))
                                {
                                    timingBatch.Interval = TimeSpan.Parse(batchElement.Interval);
                                }
                            }
                            if (_batchs.ContainsKey(batch.Name))
                            {
                                throw new InvalidOperationException("已经注册了相同名称的其他任务");
                            }
                            _batchs.Add(batch.Name, batch);
                            batch.Runtime = this;
                        }
                    }
                    break;
                }
            }
        }

        public BatchRuntime(string section, string name) : this(null, section, name)
        {

        }

        private void Initialize(string name, int maxThreadCount)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name", "引擎名称不能为空");
            }
            //0表示不限制
            if (MaxThreadCount < 0)
            {
                throw new ArgumentException("最大线程计数\"maxThreadCount\"不能为负值");
            }
            Name = name;
            MaxThreadCount = maxThreadCount;
            _batchThreads = new List<BatchThread>(MaxThreadCount);
            _waitingBatchInstances = new Queue<BatchInstance>();
            _batchs = new Dictionary<string, Batch>();
        }

        public void Open()
        {
            lock(_scheduleLock)
            {
                if (_scheduling)
                {
                    return;
                }
                _scheduling = true;
                _waitHandle = new AutoResetEvent(false);
                _scheduleThread = new Thread(Schedule);
                _scheduleThread.Start();
            }
        }

        public void Close()
        {
            lock(_scheduleLock)
            {
                if (!_scheduling)
                {
                    return;
                }
                //停止调度线程
                _scheduling = false;
                _waitHandle.Set();
                Monitor.Exit(_scheduleLock);
                while (!_scheduleThread.Join(5000))
                {
                    _waitHandle.Set();
                }
                Monitor.Enter(_scheduleLock);
                _waitHandle.Close();
                _waitHandle = null;
            }
            lock (_batchsLock)
            {
                //取消所有任务的实例
                foreach (KeyValuePair<string, Batch> item in _batchs)
                {
                    item.Value.CancelAll("引擎关闭");
                }
                _batchs.Clear();
            }
        }

        /// <summary>
        /// 调度线程方法（当前版本仅负责调度周期性的任务）
        /// </summary>
        /// <param name="state"></param>
        private void Schedule(object state)
        {
        ScheduleBegin:
            try
            {
                lock (_scheduleLock)
                {
                    while (_scheduling)
                    {
                        DateTime latestDueTime = ScheduleTimingBatchs();
                        if (latestDueTime < DateTime.MaxValue)
                        {
                            TimeSpan delay = latestDueTime - DateTime.Now;
                            if (delay > TimeSpan.Zero)
                            {
                                //延迟等待的时间大于0，开始等待
                                Monitor.Exit(_scheduleLock);
                                _waitHandle.WaitOne(delay);
                                Monitor.Enter(_scheduleLock);
                            }
                        }
                        else
                        {
                            //无限期等待下个信号
                            Monitor.Exit(_scheduleLock);
                            _waitHandle.WaitOne(TimeSpan.FromMilliseconds(Timeout.Infinite));
                            Monitor.Enter(_scheduleLock);
                        }
                    }
                }
            }
            catch (ThreadAbortException ex)
            {
                //记录引擎日志
                OnLog(ex);
                Thread.ResetAbort();
                goto ScheduleBegin;
            }
            catch (ThreadInterruptedException ex)
            {
                //记录引擎日志
                OnLog(ex);
                goto ScheduleBegin;
            }
            catch (Exception ex)
            {
                //记录引擎日志
                OnLog(ex);
                goto ScheduleBegin;
            }
        }

        /// <summary>
        /// 调度周期性任务
        /// </summary>
        /// <returns></returns>
        private DateTime ScheduleTimingBatchs()
        {
            //记录本次执行的时间
            DateTime currentTime = DateTime.Now;
            //最近一个任务的到期时间
            DateTime latestDueTime = DateTime.MaxValue;
            lock (_batchsLock)
            {
                foreach (KeyValuePair<string, Batch> item in _batchs)
                {
                    if (!(item.Value is TimingBatch))
                    {
                        //非定时任务
                        continue;
                    }
                    TimingBatch timingBatch = (TimingBatch)item.Value;
                    if (timingBatch.StartTime < currentTime
                        && timingBatch.Interval <= TimeSpan.Zero)
                    {
                        //启动时间已过，且周期无效
                        //无效周期统一按Timeout.Infinite处理，即永不到期
                        continue;
                    }
                    if (default(DateTime).Equals(timingBatch.NextDueTime))
                    {
                        //尚未执行，计算下次到期时间
                        if (timingBatch.StartTime < currentTime)
                        {
                            //加上一定的周期间隔，使下次执行时间刚好超过当前时间
                            timingBatch.NextDueTime = timingBatch.StartTime
                                + new TimeSpan(((long)Math.Ceiling((double)(currentTime - timingBatch.StartTime).Ticks / timingBatch.Interval.Ticks)) * timingBatch.Interval.Ticks);
                        }
                        else
                        {
                            timingBatch.NextDueTime = timingBatch.StartTime;
                        }
                    }
                    if (timingBatch.NextDueTime <= currentTime)
                    {
                        //任务下次执行时间到期，则调度执行
                        if (timingBatch.Interval > TimeSpan.Zero)
                        {
                            //更新下次到期时间
                            timingBatch.NextDueTime += timingBatch.Interval;
                        }
                        BatchInstance instance = item.Value.CreateInstance();
                        instance.Execute(null);
                    }
                    //比较到期时间
                    if (timingBatch.NextDueTime < latestDueTime)
                    {
                        latestDueTime = timingBatch.NextDueTime;
                    }
                }
            }
            return latestDueTime;
        }

        /// <summary>
        /// 调度下个排队任务实例（当前版本调度可由用户调用或者任务实例执行线程发起）
        /// </summary>
        /// <param name="schedulingThread">发起调度的线程(为空则说明是由非任务实例线程发起的调度)</param>
        internal void ScheduleWaitingInstances(BatchThread schedulingThread)
        {
            lock(_scheduleLock)
            {
                if (schedulingThread != null)
                {
                    _batchThreads.Remove(schedulingThread);
                }
                if (!_scheduling)
                {
                    return;
                }
                lock(_waitingBatchInstancesLock)
                {
                    while (_waitingBatchInstances.Count > 0)
                    {
                        BatchInstance item = _waitingBatchInstances.Peek();
                        if (item.State == BatchInstanceState.Cancelled)
                        {
                            _waitingBatchInstances.Dequeue();
                            continue;
                        }
                        if (MaxThreadCount > 0 && _batchThreads.Count >= MaxThreadCount)
                        {
                            OnLog(string.Format("达到最大线程计数，尚余{0}个任务实例未调度。"
                                , _waitingBatchInstances.Count.ToString()));
                            break;
                        }
                        item = _waitingBatchInstances.Dequeue();
                        BatchThread thread = new BatchThread();
                        thread.Runtime = this;
                        _batchThreads.Add(thread);
                        thread.Execute(item);
                    }
                }
            }
        }

        internal void Execute(BatchInstance instance)
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }
            lock (_waitingBatchInstancesLock)
            {
                instance.State = BatchInstanceState.Waiting;
                instance.WaitTime = DateTime.Now;
                _waitingBatchInstances.Enqueue(instance);
            }
            try
            {
                instance.Batch.InstanceStateChange(instance);
            }
            finally
            {
                ScheduleWaitingInstances(null);
            }
        }

        internal void Cancel(BatchInstance instance)
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }
            if (instance.State == BatchInstanceState.Initialized
                || instance.State == BatchInstanceState.Waiting)
            {
                lock (_waitingBatchInstancesLock)
                {
                    foreach (BatchInstance item in _waitingBatchInstances)
                    {
                        if (item == instance)
                        {
                            item.State = BatchInstanceState.Cancelled;
                            item.EndTime = DateTime.Now;
                            item.Batch.InstanceStateChange(item);
                            break;
                        }
                    }
                }
            }
            else if (instance.State == BatchInstanceState.Executing)
            {
                instance.Thread.Cancel();
            }
        }

        internal void OnLog(string text)
        {
            if (Log != null)
            {
                Log(this, new LogEventArgs(text));
            }
        }

        internal void OnLog(Exception ex)
        {
            if (Log != null)
            {
                Log(this, new LogEventArgs(ex));
            }
        }

        public void Register(Type batchType, params object[] args)
        {
            if (!batchType.IsSubclassOf(typeof(Batch)))
            {
                throw new ArgumentException("batchType", "指定的任务类型必须从Batch基类派生");
            }
            Batch batch = (Batch)Activator.CreateInstance(batchType, args);
            lock (_batchsLock)
            {
                if (_batchs.ContainsKey(batch.Name))
                {
                    throw new InvalidOperationException(string.Format("已经注册了名为\"{0}\"的任务", batch.Name));
                }
                else
                {
                    _batchs.Add(batch.Name, batch);
                    batch.Runtime = this;
                }
            }
            if (_scheduling
                && batchType.IsSubclassOf(typeof(TimingBatch)))
            {
                //唤醒任务调度
                EventWaitHandle waitHandle = _waitHandle;
                if (waitHandle != null)
                {
                    try
                    {
                        waitHandle.Set();
                    }
                    catch
                    {
                        //在引擎关闭后尝试调度，忽略此异常
                    }
                }
            }
        }

        public void Unregister(string batchName)
        {
            if (string.IsNullOrEmpty(batchName))
            {
                return;
            }
            if (_batchs.ContainsKey(batchName))
            {
                lock (_batchsLock)
                {
                    if (_batchs.ContainsKey(batchName))
                    {
                        Batch batch = _batchs[batchName];
                        batch.CancelAll("任务注销");
                        _batchs.Remove(batchName);
                        batch.Runtime = null;
                    }
                }
            }
        }

        public Batch Get(string batchName)
        {
            if (!_batchs.ContainsKey(batchName))
            {
                return null;
            }
            lock(_batchsLock)
            {
                if (!_batchs.ContainsKey(batchName))
                {
                    return null;
                }
                return _batchs[batchName];
            }
        }

        public BatchInstance CreateInstance(string batchName)
        {
            if (string.IsNullOrEmpty(batchName))
            {
                throw new ArgumentNullException("batchName", "任务名称不能为空");
            }
            if (!_scheduling)
            {
                throw new InvalidOperationException("引擎未处于启动状态");
            }
            Batch batch = Get(batchName);
            if (batch == null)
            {
                throw new InvalidOperationException(string.Format("指定名称\"{0}\"的任务尚未注册", batchName));
            }
            BatchInstance instance = batch.CreateInstance();
            return instance;
        }

        public BatchInstance GetInstance(string batchName, Guid instanceId)
        {
            Batch batch = null;
            if (!_batchs.ContainsKey(batchName))
            {
                return null;
            }
            lock (_batchsLock)
            {
                if (_batchs.ContainsKey(batchName))
                {
                    batch = _batchs[batchName];
                }
            }
            if (batch == null)
            {
                return null;
            }
            return batch.Get(instanceId);
        }

        public BatchInstance[] GetInstanceList(string batchName)
        {
            Batch batch = null;
            if (!_batchs.ContainsKey(batchName))
            {
                return null;
            }
            lock (_batchsLock)
            {
                if (_batchs.ContainsKey(batchName))
                {
                     batch = _batchs[batchName];
                }
            }
            if (batch == null)
            {
                return null;
            }
            return batch.GetList();
        }
    }
}